package com.easypay.reactive8583.netty.pipeline;

import com.easypay.reactive8583.IsoMessageListener;
import com.solab.iso8583.IsoMessage;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author pandans
 * @date 2021/03/04 15:12
 * @Description:
 */
@ChannelHandler.Sharable
public class CompositeIsoMessageHandler<T extends IsoMessage> extends ChannelInboundHandlerAdapter {
    private boolean failOnError = true;
    private Logger logger = LoggerFactory.getLogger(CompositeIsoMessageHandler.class);

    public CompositeIsoMessageHandler(boolean failOnError) {
        this.failOnError = failOnError;
    }

    public CompositeIsoMessageHandler() {
        super();
    }

    private List<IsoMessageListener<T>> messageListeners = new CopyOnWriteArrayList<>();

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        T isoMessage = (T) msg;
        doHandleMessage(ctx, isoMessage);
        super.channelRead(ctx, msg);
    }
    private void doHandleMessage(ChannelHandlerContext ctx, T isoMessage){
        boolean applyNextListener = true;
        int size = messageListeners.size();
        int i = 0;
        while (applyNextListener && i < size) {
            IsoMessageListener<T> messageListener = messageListeners.get(i);
            applyNextListener = handleWithMessageListener(
                    messageListener, isoMessage, ctx
            );
            if (!applyNextListener) {
                logger.trace(
                        "Stopping further procession of message {} after handler {}",
                        isoMessage,
                        messageListener
                );
            }
            i++;
        }
    }

    private Boolean handleWithMessageListener(IsoMessageListener<T> messageListener, T isoMessage, ChannelHandlerContext ctx) {
        try {
            if (messageListener.applies(isoMessage)) {
                logger.debug(
                        "Handling IsoMessage[@type=0x{}] with {}",
                        String.format("%04X", isoMessage.getType()),
                        messageListener
                );
                return messageListener.onMessage(ctx, isoMessage);
            }
        } catch (Exception e) {
            logger.debug(
                    "Can't evaluate {}.apply({})",
                    messageListener, isoMessage.getClass(), e
            );
            if (failOnError) {
                throw e;
            }
        }
        return true;
    }

    public void addListener(IsoMessageListener<T> listener) {
        messageListeners.add(listener);
    }

    @SafeVarargs
    public final void addListeners(IsoMessageListener<T>... listeners) {
        for (IsoMessageListener<T> listener : listeners) {
            addListener(listener);
        }
    }

    public void removeListener(IsoMessageListener<T> listener) {
        messageListeners.remove(listener);
    }
}
